﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Net.NetworkInformation;

namespace LabMonitorRemote
{
    public class DewkPing
    {
        const Int32 _Port = 10001;
        string _LoggerName;
        string _IpAddress;
        IPEndPoint _IpEndPoint;
        frmMain _ParentForm;
        Boolean _Connected;
        Boolean _TerminateLoops = false;
        LoggerDownload _CallingLoggerDownload;
        
        string _Manufacturer;
        string _ModelNumber;
        string _SerialNumber;
        string _FirmwareVersion;

        #region Constructors
        public DewkPing(string IpAddress, Form ParentForm = null)
        {
            _IpAddress = IpAddress;
            _IpEndPoint = new IPEndPoint(IPAddress.Parse(IpAddress), _Port);
            _ParentForm = (frmMain)ParentForm;
        }

        public DewkPing(string LoggerName, string IpAddress, Form ParentForm = null)
        {
            _LoggerName = LoggerName;
            _IpAddress = IpAddress;
            _IpEndPoint = new IPEndPoint(IPAddress.Parse(IpAddress), _Port);
            _ParentForm = (frmMain)ParentForm;
        }
        
        public DewkPing(DewkLogger Dewk, Form ParentForm = null)
        {
            this.LoggerName = Dewk.LoggerName;
            this.IpAddress = Dewk.IpAddress;
            this.IpEndPoint = new IPEndPoint(IPAddress.Parse(_IpAddress), _Port);
            this.ParentForm = (frmMain)ParentForm;
        }
        #endregion Constructors


        #region Properties
        public frmMain ParentForm
        {
            get { return _ParentForm; }
            set { _ParentForm = value; }
        }

        public string LoggerName
        {
            get { return _LoggerName; }
            set { _LoggerName = value; }
        }

        public Boolean IsConnected
        {
            get { return _Connected; }
            private set 
            {
                _Connected = value;
                OnConnectionDetermined();
            }
        }

        public string Manufacturer
        {
            get { return _Manufacturer; }
            private set { _Manufacturer = value; }
        }

        public string ModelNumber
        {
            get { return _ModelNumber; }
            private set { _ModelNumber = value; }
        }

        public string SerialNumber
        {
            get { return _SerialNumber; }
            private set { _SerialNumber = value; }
        }

        public string FirmwareVersion
        {
            get { return _FirmwareVersion; }
            private set { _FirmwareVersion = value; }
        }

        public IPEndPoint IpEndPoint
        {
            get { return _IpEndPoint; }
            private set
            {
                _IpEndPoint = value;
                _IpAddress = value.Address.ToString();
            }
        }

        public string IpAddress
        {
            get { return _IpAddress; }
            set
            {
                if (ValidIPAddress(value))
                {
                    _IpAddress = value;
                }
                else
                {
                    MessageBox.Show(value + " is not a valid IPv4 Address", "Invalid IP Address");
                }
            }
        }

        private bool ValidIPAddress(string ip)
        {
            string[] octets = ip.Split(".".ToCharArray());
            if (octets.Length < 4)
            {
                // not a valid IPv4 string in X.X.X.X format
                return false;
            }
            else
                foreach (string octet in octets)
                {
                    byte checkPart = 0;
                    if (!byte.TryParse(octet, out checkPart))
                    {
                        // not a valid IPv4 string in X.X.X.X format
                        return false;
                    }
                }
            // string is a valid IPv4 string in X.X.X.X format
            return true;
        }

        public LoggerDownload CallingLoggerDownload
        {
            get { return _CallingLoggerDownload; }
            set 
            {
                _CallingLoggerDownload = value;
                _CallingLoggerDownload.DownloadCanceled += new LoggerDownload.DownloadCanceledEventHandler(TerminateAllLoops);
                _CallingLoggerDownload.ConnectionFailed += new LoggerDownload.ConnectionFailedEventHandler(TerminateAllLoops);
            }
        }

        private Boolean TerminateLoops
        {
            get { return _TerminateLoops; }
            set
            {
                if (_TerminateLoops != value)
                    _TerminateLoops = value;
            }
        }
        #endregion Properties

        #region Events
        public event ConnectionDeterminedEventHandler ConnectionDetermined;
        public delegate void ConnectionDeterminedEventHandler(DewkPing dp, EventArgs e);
        private void OnConnectionDetermined()
        {
            if (ConnectionDetermined != null)
            {
                ConnectionDetermined(this, new EventArgs());
            }
        }
        #endregion Events

        private void ShowNetworkWrite(DewkCommand dc, EventArgs e)
        {
            if (this.ParentForm != null)
                this.ParentForm.WriteNetworkFeedback(dc.IpEndPoint.Address.ToString() + ": " + dc.CommandToSend);
        }

        private void ShowNetworkRead(DewkRead dr, EventArgs e)
        {
            if (this.ParentForm != null)
                this.ParentForm.WriteNetworkFeedback(_LoggerName, dr.ReadBuffer, dr.NumBytesRead);
        }

        private void TerminateAllLoops(object sender, EventArgs e)
        {
            TerminateLoops = true;
        }

        private Boolean PingLogger()
        {
            Ping ping = new Ping();
            try
            {
                PingReply pingReply = ping.Send(IpAddress);
                if (pingReply.Status == IPStatus.Success)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception e)
            {
                this.IsConnected = false;
                //MessageBox.Show(e.ToString());
                return false;
            }

        }

        public void TestCommunications()
        {
            if (PingLogger())
            {
                GetIDN();
            }
            else
            {
                PrintLoggerNotConnected();
                this.IsConnected = false;
            }
        }

        public void GetIDN()
        {
            IDNDewkCommand RequestIDN = new IDNDewkCommand(_IpEndPoint);
            RequestIDN.CommandSent += new DewkCommand.CommandSentEventHandler(ShowNetworkWrite);
            RequestIDN.WriteCommand();
            IDNDewkRead ReadIDN = new IDNDewkRead(RequestIDN.TCPClient, RequestIDN.NetStream);
            ReadIDN.ReadComplete += new DewkRead.ReadCompleteEventHandler(ShowNetworkRead);
            ReadIDN.Read();

            if (!DecodeIDNBytes(ReadIDN.ReadBuffer, ReadIDN.NumBytesRead) && !TerminateLoops)
                //GetIDN();
                TestCommunications();

        }
        private Boolean DecodeIDNBytes(byte[] array, Int32 length)
        {
            char[] IdnArray;
            string IdnString = "";
            IdnArray = System.Text.Encoding.ASCII.GetChars(array);

            for (Int32 i = 0; i < length; i++)
            {
                IdnString += IdnArray[i].ToString();
            }
            if (LoadIdnReplyComponents(IdnString))
            {
                //this.ParentForm.WriteNetworkFeedback(IdnString);
                PrintLoggerInfo();
                return true;
            }
            else
            {
                return false;
            }
        }
        private Boolean LoadIdnReplyComponents(string idn)
        {
            string[] components = idn.Split(",".ToCharArray());

            if (components.Length < 4)
            {
                return false;
            }
            else
            {
                this.Manufacturer = components[0];
                this.ModelNumber = components[1];
                this.SerialNumber = components[2];
                this.FirmwareVersion = components[3];
                this.IsConnected = true;
                return true;
            }
        }
        private void PrintLoggerInfo()
        {
            if (this.ParentForm != null)
            {
                string message = string.Format(
                    this.LoggerName + ": " + this.IpAddress + " Is connected and responding properly!" + Environment.NewLine +
                    "\tManufacturer: " + this.Manufacturer + Environment.NewLine +
                    "\tModel :" + this.ModelNumber + Environment.NewLine +
                    "\tSerial: " + this.SerialNumber + Environment.NewLine +
                    "\tFirmware: " + this.FirmwareVersion);
                this.ParentForm.WriteNetworkFeedback(message);
            }
        }

        private void PrintLoggerNotConnected()
        {
            if (this.ParentForm != null)
            {
                string message = string.Format(
                    this.LoggerName + " " + this.IpAddress + ": Is not responding to network calls");
                this.ParentForm.WriteNetworkFeedback(message);
            }
       }
    }
}
